﻿namespace block_explorer.NoiseSystem.Nodes
{
    class Select : INode
    {
        INode mHighSource;
        INode mLowSource;
        INode mControlSource;
        INode mThreshold;
        INode mFalloff;

        public Select(INode controlSource, INode highSource, INode lowSource, INode threshold, INode falloff)
        {
            mControlSource = controlSource;
            mHighSource = highSource;
            mLowSource = lowSource;
            mThreshold = threshold;
            mFalloff = falloff;

            if (mFalloff == null)
                mFalloff = new Nodes.Constant(0.0);
        }

        public Select(INode controlSource, INode highSource, INode lowSource, double threshold, double falloff)
        {
            mControlSource = controlSource;
            mHighSource = highSource;
            mLowSource = lowSource;
            mThreshold = new Constant(threshold);
            mFalloff = new Constant(falloff);
        }

        public Select(INode controlSource, double highSource, double lowSource, double threshold, double falloff)
        {
            mControlSource = controlSource;
            mHighSource = new Constant(highSource);
            mLowSource = new Constant(lowSource);
            mThreshold = new Constant(threshold);
            mFalloff = new Constant(falloff);
        }

        public double Value(double x, double y, double z)
        {
            double control = mControlSource.Value(x, y, z);
            double falloff = mFalloff.Value(x, y, z);
            double threshold = mThreshold.Value(x, y, z);

            if (falloff > 0.0)
            {
                if (control < (threshold - falloff))
                {
                    return mLowSource.Value(x, y, z);
                }
                else if (control > (threshold + falloff))
                {
                    return mHighSource.Value(x, y, z);
                }
                else
                {
                    double lower = threshold - falloff;
                    double upper = threshold + falloff;
                    double blend = quintic_blend((control - lower) / (upper - lower));
                    return lerp(blend, mLowSource.Value(x, y, z), mHighSource.Value(x, y, z));
                }
            }
            else
            {
                if (control < threshold) return mLowSource.Value(x, y, z);
                else return mHighSource.Value(x, y, z);
            }
        }

        double quintic_blend(double t)
        {
            return t*t*t*(t*(t*6-15)+10);
        }

        double lerp(double t, double a, double b)
        {
            return a+t*(b-a);
        }
    }
}
